refactor: move identities, sub-addressing and multi-account options onto the account page

This commit is contained in:
Linus Rath
2026-10-19 17:44:11 +02:00
parent 5b1d056eef
commit b1fa88841d
38 changed files with 213 additions and 90 deletions
@@ -84,7 +84,7 @@ export function AccountArchiveSettings() {
};
return (
<SettingsSection title={t('title')} description={t(passwordsAllowed ? 'description_passwords' : 'description')}>
<SettingsSection id="backup" advanced title={t('title')} description={t(passwordsAllowed ? 'description_passwords' : 'description')}>
<p className="text-sm text-muted-foreground">
{saved === null ? '…' : t(saved ? 'status_saved' : 'status_none', { account: toUnicodeEmail(owner.username) })}
</p>
@@ -146,7 +146,11 @@ function PasswordChangeSection() {
);
}
function DisplayNameSection() {
/**
* Editable display name (the Stalwart principal's name). Rendered by the
* Account page's Profile section; the Security page no longer repeats it.
*/
export function DisplayNameSection() {
const t = useTranslations('settings.security');
const { displayName, updateDisplayName, isSaving, isLoadingPrincipal } = useAccountSecurityStore();
const [name, setName] = useState(displayName);
@@ -1508,8 +1512,6 @@ export function AccountSecuritySettings() {
<>
<PasswordChangeSection />
<div className="border-t border-border" />
<DisplayNameSection />
<div className="border-t border-border" />
<div>
<div className="flex items-center gap-2 mb-3">
<Shield className="w-4 h-4 text-muted-foreground" />
+37 -9
View File
@@ -1,6 +1,6 @@
"use client";
import { useState, useRef, useCallback, useMemo } from 'react';
import { useState, useRef, useCallback, useMemo, useEffect } from 'react';
import { useTranslations } from 'next-intl';
import { Check, GripVertical, Plus, Star, AlertCircle, ChevronRight } from '@/components/icons';
import { useAuthStore } from '@/stores/auth-store';
@@ -18,6 +18,10 @@ import { toUnicodeDomain, toUnicodeEmail } from '@/lib/idn';
import { IS_LITE } from '@/lib/lite';
import { usePolicyStore } from '@/stores/policy-store';
import { AccountArchiveSettings } from './account-archive-settings';
import { DisplayNameSection } from './account-security-settings';
import { EmailAddressesSection } from './identity-settings';
import { MultipleAccountsSection } from './layout-settings';
import { useAccountSecurityStore } from '@/stores/account-security-store';
function hostnameOf(serverUrl: string): string {
try { return toUnicodeDomain(new URL(serverUrl).hostname); } catch { return serverUrl; }
@@ -42,6 +46,22 @@ export function AccountSettings() {
const switchAccount = useAuthStore((s) => s.switchAccount);
const setManagedAccount = useManagedAccountStore((s) => s.setManagedAccount);
const archiveEnabled = usePolicyStore((s) => s.isFeatureEnabled('accountArchiveEnabled')) && !IS_LITE && !isDemoMode;
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
// The display name is editable on Stalwart (the principal's name) for
// password sign-ins; OAuth and token sessions cannot manage the principal.
const { isStalwart, probe, fetchPrincipal } = useAccountSecurityStore();
const canEditDisplayName = isStalwart === true && authMode !== 'oauth' && authMode !== 'token';
// Wait for `client` before probing, as the Security page does: on reload
// the persisted session flips authenticated before the client reconnects.
useEffect(() => {
if (!isAuthenticated || !client) return;
if (isStalwart === null) {
void probe().then((detected) => { if (detected) void fetchPrincipal(); });
} else if (isStalwart) {
void fetchPrincipal();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isAuthenticated, client, isStalwart]);
// Shared/group accounts delegated to this session (excludes the user's own
// primary account). These can be drilled into for scoped settings editing.
@@ -123,11 +143,15 @@ export function AccountSettings() {
return (
<div className="space-y-8">
<SettingsSection title={t('title')} description={t('description')}>
{/* Display Name */}
<SettingItem label={t('name_label')}>
<span className="text-sm text-foreground">{displayName || tCommon('unknown')}</span>
</SettingItem>
<SettingsSection id="profile" title={t('title')} description={t('description')}>
{/* Display Name: editable where the server lets us, read-only otherwise. */}
{canEditDisplayName ? (
<DisplayNameSection />
) : (
<SettingItem label={t('name_label')}>
<span className="text-sm text-foreground">{displayName || tCommon('unknown')}</span>
</SettingItem>
)}
{/* Email Address */}
<SettingItem label={t('email.label')}>
@@ -151,7 +175,7 @@ export function AccountSettings() {
</SettingItem>
{/* Server */}
<SettingItem label={t('server.label')}>
<SettingItem label={t('server.label')} advanced>
<span className="text-sm text-foreground truncate max-w-xs">
{serverUrl || tCommon('unknown')}
</span>
@@ -191,9 +215,11 @@ export function AccountSettings() {
)}
</SettingsSection>
<EmailAddressesSection />
{/* Logged-in accounts list */}
{accounts.length > 0 && (
<SettingsSection title={t('accounts.title')} description={t('accounts.description')}>
<SettingsSection id="browser" title={t('accounts.title')} description={t('accounts.description')}>
<div className="space-y-2">
{accounts.map((a, index) => (
<AccountRow
@@ -241,10 +267,12 @@ export function AccountSettings() {
{archiveEnabled && <AccountArchiveSettings />}
<MultipleAccountsSection />
{/* Shared / group accounts delegated to this session. Clicking one drills
into a scoped settings view (filters, vacation, calendars, contacts). */}
{sharedAccounts.length > 0 && (
<SettingsSection title={t('shared_accounts.title')} description={t('shared_accounts.description')}>
<SettingsSection id="shared" title={t('shared_accounts.title')} description={t('shared_accounts.description')}>
<div className="space-y-2">
{sharedAccounts.map((acc) => {
const editable = firstScopedTab(acc.capabilities) !== null;
+1 -2
View File
@@ -5,7 +5,7 @@ import { useThemeStore } from '@/stores/theme-store';
import { useSettingsStore, type Density } from '@/stores/settings-store';
import { SettingsSection, SettingItem, RadioGroup, ToggleSwitch } from './settings-section';
import { ThemesSettings } from './themes-settings';
import { LayoutSection, SidebarSection, MultipleAccountsSection, ExperimentalSection } from './layout-settings';
import { LayoutSection, SidebarSection, ExperimentalSection } from './layout-settings';
import { cn } from '@/lib/utils';
import { useTour } from '@/components/tour/tour-provider';
import { Button } from '@/components/ui/button';
@@ -139,7 +139,6 @@ export function AppearanceSettings() {
<LayoutSection />
<SidebarSection />
<MultipleAccountsSection />
<SettingsSection id="tour" title={tTour('settings_section')}>
<SettingItem label={tTour('restart_title')} description={tTour('restart_desc')}>
@@ -8,14 +8,6 @@ import { useAuthStore } from '@/stores/auth-store';
import { usePolicyStore } from '@/stores/policy-store';
import { SettingsSection, SettingItem, Select, ToggleSwitch } from './settings-section';
import { X } from '@/components/icons';
import {
SUPPORTED_SUB_ADDRESS_DELIMITERS,
isSupportedSubAddressDelimiter,
isValidSubAddressDelimiter,
} from '@/lib/sub-addressing';
const CUSTOM_DELIMITER_SENTINEL = '__custom__';
const DEFAULT_CUSTOM_DELIMITER = '~';
export function ComposingSettings() {
const t = useTranslations('settings.email_behavior');
@@ -32,7 +24,6 @@ export function ComposingSettings() {
recipientMentionsEnabled,
mentionAddsRecipientTo,
sendDelaySeconds,
subAddressDelimiter,
signaturePosition,
signatureSeparatorEnabled,
requestReadReceiptDefault,
@@ -135,49 +126,6 @@ export function ComposingSettings() {
/>
</SettingItem>
<SettingItem
label={t('sub_address_delimiter.label')}
description={t('sub_address_delimiter.description', { delimiter: subAddressDelimiter })}
>
<div className="flex flex-col items-end gap-2">
<Select
value={isSupportedSubAddressDelimiter(subAddressDelimiter) ? subAddressDelimiter : CUSTOM_DELIMITER_SENTINEL}
onChange={(value) => {
if (value === CUSTOM_DELIMITER_SENTINEL) {
if (isSupportedSubAddressDelimiter(subAddressDelimiter)) {
updateSetting('subAddressDelimiter', DEFAULT_CUSTOM_DELIMITER);
}
} else {
updateSetting('subAddressDelimiter', value);
}
}}
options={[
...SUPPORTED_SUB_ADDRESS_DELIMITERS.map((delim) => ({
value: delim,
label: t('sub_address_delimiter.option', { delimiter: delim }),
})),
{ value: CUSTOM_DELIMITER_SENTINEL, label: t('sub_address_delimiter.custom') },
]}
/>
{!isSupportedSubAddressDelimiter(subAddressDelimiter) && (
<input
type="text"
maxLength={1}
value={subAddressDelimiter}
onChange={(e) => {
const next = e.target.value.slice(0, 1);
if (next && isValidSubAddressDelimiter(next)) {
updateSetting('subAddressDelimiter', next);
}
}}
aria-label={t('sub_address_delimiter.custom_input_label')}
placeholder={DEFAULT_CUSTOM_DELIMITER}
className="w-16 px-2 py-1 text-sm font-mono text-center bg-background border border-border rounded-md focus:outline-none focus:ring-1 focus:ring-ring"
/>
)}
</div>
</SettingItem>
<SettingItem label={t('empty_subject_warning.label')} description={t('empty_subject_warning.description')}>
<ToggleSwitch
checked={emptySubjectWarningEnabled}
+66 -3
View File
@@ -3,18 +3,36 @@
import { useState } from 'react';
import { useTranslations } from 'next-intl';
import { Button } from '@/components/ui/button';
import { SettingsSection, SettingItem } from './settings-section';
import { SettingsSection, SettingItem, Select } from './settings-section';
import { IdentityManagerModal } from '@/components/identity/identity-manager-modal';
import { useIdentityStore } from '@/stores/identity-store';
import { useSettingsStore } from '@/stores/settings-store';
import {
SUPPORTED_SUB_ADDRESS_DELIMITERS,
isSupportedSubAddressDelimiter,
isValidSubAddressDelimiter,
} from '@/lib/sub-addressing';
export function IdentitySettings() {
const CUSTOM_DELIMITER_SENTINEL = '__custom__';
const DEFAULT_CUSTOM_DELIMITER = '~';
/**
* The Account page's "Email addresses" section: the identities you send
* from (each with its name and signature), sub-addressing and, with
* Advanced on, the delimiter the server uses for it. Was its own
* Identities tab, with the delimiter over on Composing.
*/
export function EmailAddressesSection() {
const t = useTranslations('settings.identities');
const tAccount = useTranslations('settings.account');
const tEmail = useTranslations('settings.email_behavior');
const { identities } = useIdentityStore();
const { subAddressDelimiter, updateSetting } = useSettingsStore();
const [showModal, setShowModal] = useState(false);
return (
<>
<SettingsSection title={t('title')} description={t('description')}>
<SettingsSection id="addresses" title={tAccount('sections.addresses')} description={t('description')}>
{/* Identity Count */}
<SettingItem
label={t('identities_count.label')}
@@ -43,6 +61,51 @@ export function IdentitySettings() {
{t('sub_addressing.learn_more')}
</Button>
</SettingItem>
<SettingItem
nested
advanced
label={tEmail('sub_address_delimiter.label')}
description={tEmail('sub_address_delimiter.description', { delimiter: subAddressDelimiter })}
>
<div className="flex flex-col items-end gap-2">
<Select
value={isSupportedSubAddressDelimiter(subAddressDelimiter) ? subAddressDelimiter : CUSTOM_DELIMITER_SENTINEL}
onChange={(value) => {
if (value === CUSTOM_DELIMITER_SENTINEL) {
if (isSupportedSubAddressDelimiter(subAddressDelimiter)) {
updateSetting('subAddressDelimiter', DEFAULT_CUSTOM_DELIMITER);
}
} else {
updateSetting('subAddressDelimiter', value);
}
}}
options={[
...SUPPORTED_SUB_ADDRESS_DELIMITERS.map((delim) => ({
value: delim,
label: tEmail('sub_address_delimiter.option', { delimiter: delim }),
})),
{ value: CUSTOM_DELIMITER_SENTINEL, label: tEmail('sub_address_delimiter.custom') },
]}
/>
{!isSupportedSubAddressDelimiter(subAddressDelimiter) && (
<input
type="text"
maxLength={1}
value={subAddressDelimiter}
onChange={(e) => {
const next = e.target.value.slice(0, 1);
if (next && isValidSubAddressDelimiter(next)) {
updateSetting('subAddressDelimiter', next);
}
}}
aria-label={tEmail('sub_address_delimiter.custom_input_label')}
placeholder={DEFAULT_CUSTOM_DELIMITER}
className="w-16 px-2 py-1 text-sm font-mono text-center bg-background border border-border rounded-md focus:outline-none focus:ring-1 focus:ring-ring"
/>
)}
</div>
</SettingItem>
</SettingsSection>
{/* Identity Manager Modal */}
+1 -5
View File
@@ -12,7 +12,6 @@ import {
Search,
User,
Shield,
UserPen,
PalmtreeIcon,
Calendar,
Filter,
@@ -44,7 +43,6 @@ import { ReadingSettings } from '@/components/settings/reading-settings';
import { MessageListSettings } from '@/components/settings/message-list-settings';
import { ComposingSettings } from '@/components/settings/composing-settings';
import { AccountSettings } from '@/components/settings/account-settings';
import { IdentitySettings } from '@/components/settings/identity-settings';
import { VacationSettings } from '@/components/settings/vacation-settings';
import { CalendarSettings } from '@/components/settings/calendar-settings';
import { CalendarManagementSettings } from '@/components/settings/calendar-management-settings';
@@ -134,7 +132,6 @@ const tabIcons: Record<Tab, AppIcon> = {
reading: BookOpen,
composing: PenLine,
downloads: Download,
identities: UserPen,
vacation: PalmtreeIcon,
filters: Filter,
templates: FileText,
@@ -162,6 +159,7 @@ export const LEGACY_TAB_MAP: Record<string, Tab> = {
layout: 'appearance',
themes: 'appearance',
content_senders: 'reading',
identities: 'account',
};
function readPersistedTab(): SettingsTabId {
@@ -571,7 +569,6 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
{ id: 'reading', label: t('tabs.reading'), icon: tabIcons.reading, group: 'mail' },
{ id: 'composing', label: t('tabs.composing'), icon: tabIcons.composing, group: 'mail' },
{ id: 'downloads', label: t('tabs.downloads'), icon: tabIcons.downloads, group: 'mail' },
{ id: 'identities', label: t('tabs.identities'), icon: tabIcons.identities, group: 'mail' },
...(supportsVacation ? [{ id: 'vacation' as Tab, label: t('tabs.vacation'), icon: tabIcons.vacation, group: 'mail' as TabGroup }] : []),
...(supportsSieve ? [{ id: 'filters' as Tab, label: t('tabs.filters'), icon: tabIcons.filters, group: 'mail' as TabGroup }] : []),
...(isFeatureEnabled('templatesEnabled') ? [{ id: 'templates' as Tab, label: t('tabs.templates'), icon: tabIcons.templates, group: 'mail' as TabGroup }] : []),
@@ -715,7 +712,6 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
{effectiveActiveTab === 'reading' && <ReadingSettings />}
{effectiveActiveTab === 'composing' && <ComposingSettings />}
{effectiveActiveTab === 'downloads' && <DownloadsSettings />}
{effectiveActiveTab === 'identities' && <IdentitySettings />}
{effectiveActiveTab === 'vacation' && <VacationSettings />}
{effectiveActiveTab === 'filters' && <FilterSettings />}
{effectiveActiveTab === 'templates' && <TemplateSettings />}
+2 -1
View File
@@ -93,7 +93,8 @@ describe('settings search index (English messages)', () => {
expect(labelsFor('composing')).toContain('Undo send / send delay');
expect(labelsFor('composing')).toContain('Request read receipts by default');
expect(labelsFor('appearance')).toContain('Pro Interface (Experimental)');
expect(labelsFor('appearance')).toContain('Tell accounts apart');
expect(labelsFor('account')).toContain('Tell accounts apart');
expect(labelsFor('account')).toContain('Your Identities');
expect(labelsFor('appearance')).toContain('Themes');
expect(labelsFor('language')).toContain('Time zone');
});
+2 -2
View File
@@ -38,10 +38,10 @@ import { RELEASE_VERSION } from '@/lib/release-version';
* opens the page that holds the setting now (the settings page migrates
* the id). Listed so an unknown tab is refused here.
*/
const LEGACY_SETTINGS_TABS = ['layout', 'themes', 'content_senders'] as const;
const LEGACY_SETTINGS_TABS = ['layout', 'themes', 'content_senders', 'identities'] as const;
const SETTINGS_TABS: readonly (SettingsSearchTab | typeof LEGACY_SETTINGS_TABS[number])[] = [
'account', 'language', 'notifications', 'appearance', 'message_list', 'reading',
'composing', 'downloads', 'identities', 'vacation', 'filters', 'templates',
'composing', 'downloads', 'vacation', 'filters', 'templates',
'folders', 'keywords', 'security', 'calendar', 'contacts',
'files', 'protocol_handlers', 'sidebar_apps', 'about_data',
'plugins', 'debug',
+14 -12
View File
@@ -11,7 +11,6 @@ export type SettingsSearchTab =
| 'reading'
| 'composing'
| 'downloads'
| 'identities'
| 'vacation'
| 'filters'
| 'templates'
@@ -36,13 +35,25 @@ type Tab = SettingsSearchTab;
export const tabSearchPaths: Record<Tab, string[]> = {
account: [
'settings.account.name_label',
'settings.security.display_name',
'settings.account.username_label',
'settings.account.account_type_label',
'settings.account.auth_method_label',
'settings.account.email',
'settings.account.server',
'settings.account.storage',
'settings.identities',
'settings.email_behavior.sub_address_delimiter',
'settings.account.accounts',
'settings.account.backup',
'settings.appearance.unified_mailbox',
'settings.appearance.cross_unread',
'settings.appearance.cross_starred',
'settings.appearance.cross_all',
'settings.appearance.all_mail',
'settings.appearance.account_marker',
'settings.appearance.show_shared_mail_accounts',
'settings.account.shared_accounts',
],
language: ['settings.language_region'],
notifications: ['settings.notifications'],
@@ -61,13 +72,6 @@ export const tabSearchPaths: Record<Tab, string[]> = {
'settings.appearance.hide_account_switcher',
'settings.appearance.show_rail_account_list',
'settings.appearance.favicon_unread_badge',
'settings.appearance.unified_mailbox',
'settings.appearance.cross_unread',
'settings.appearance.cross_starred',
'settings.appearance.cross_all',
'settings.appearance.all_mail',
'settings.appearance.account_marker',
'settings.appearance.show_shared_mail_accounts',
'tour.restart_title',
'tour.show_on_new_devices_title',
'settings.appearance.pro_interface',
@@ -120,7 +124,6 @@ export const tabSearchPaths: Record<Tab, string[]> = {
'settings.email_behavior.sub_address_delimiter',
],
downloads: ['settings.downloads'],
identities: ['settings.identities'],
vacation: ['settings.vacation'],
filters: ['settings.filters'],
templates: ['settings.templates'],
@@ -143,15 +146,14 @@ export const tabSearchPaths: Record<Tab, string[]> = {
export const archiveActionSearchPaths: ReadonlySet<string> = new Set(['settings.email_behavior.archive_mode']);
export const tabKeywords: Record<Tab, string> = {
account: 'profile email password user signin signout reorder rearrange drag dropdown switcher multi-account',
account: 'profile email user signin signout reorder rearrange drag dropdown switcher multi-account identities from address signature plus addressing unified mailbox shared group',
language: 'locale region timezone date time format',
notifications: 'sound alert push badge',
appearance: 'theme dark light font size accent color animation density custom theme css skin toolbar sidebar account switcher unified mailbox icons rail layout reading pane tour',
appearance: 'theme dark light font size accent color animation density custom theme css skin toolbar sidebar account switcher icons rail layout reading pane tour',
message_list: 'inbox list preview thread conversation grouping sort order columns archive delete swipe hover favicon avatar tag color',
reading: 'mark read attachment open monospace mono font plain text images remote content block sender privacy tracking dark light',
composing: 'editor signature plain text reply forward draft compose',
downloads: 'download filename template eml attachment save export',
identities: 'from address signature email',
vacation: 'auto reply away out of office holiday responder',
filters: 'sieve rules block junk forward',
templates: 'snippet quick reply',
+3
View File
@@ -1833,6 +1833,9 @@
"title": "مشترك معي",
"description": "الحسابات الجماعية والمشتركة التي يمكنك إدارتها. اختر واحدًا لتعديل مرشحاته، والرد التلقائي أثناء الغياب، والتقويمات، وجهات الاتصال.",
"shared_label": "حساب مشترك"
},
"sections": {
"addresses": "عناوين البريد الإلكتروني"
}
},
"scoped": {
+3
View File
@@ -1833,6 +1833,9 @@
"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"
},
"sections": {
"addresses": "Adreces de correu"
}
},
"scoped": {
+3
View File
@@ -1830,6 +1830,9 @@
"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"
},
"sections": {
"addresses": "E-mailové adresy"
}
},
"security": {
+3
View File
@@ -1833,6 +1833,9 @@
"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"
},
"sections": {
"addresses": "E-mailadresser"
}
},
"security": {
+3
View File
@@ -1830,6 +1830,9 @@
"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.",
"shared_label": "Freigegebenes Konto"
},
"sections": {
"addresses": "E-Mail-Adressen"
}
},
"security": {
+3
View File
@@ -1833,6 +1833,9 @@
"title": "Shared with me",
"description": "Group and shared accounts you can manage. Select one to edit its filters, vacation responder, calendars, and contacts.",
"shared_label": "Shared account"
},
"sections": {
"addresses": "Email addresses"
}
},
"scoped": {
+3
View File
@@ -1830,6 +1830,9 @@
"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"
},
"sections": {
"addresses": "Direcciones de correo"
}
},
"security": {
+3
View File
@@ -1833,6 +1833,9 @@
"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"
},
"sections": {
"addresses": "E-posti aadressid"
}
},
"scoped": {
+3
View File
@@ -1833,6 +1833,9 @@
"title": "اشتراکی با من",
"description": "حساب‌های گروهی",
"shared_label": "حساب اشتراکی"
},
"sections": {
"addresses": "نشانی‌های ایمیل"
}
},
"scoped": {
+3
View File
@@ -1830,6 +1830,9 @@
"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é"
},
"sections": {
"addresses": "Adresses e-mail"
}
},
"security": {
+3
View File
@@ -1795,6 +1795,9 @@
"title": "משותף איתי",
"description": "חשבונות קבוצתיים ומשותפים שאתה יכול לנהל. בחר אחד כדי לערוך את המסננים, המגיב לחופשה, לוחות השנה והאנשים שלו.",
"shared_label": "חשבון משותף"
},
"sections": {
"addresses": "כתובות אימייל"
}
},
"security": {
+3
View File
@@ -1833,6 +1833,9 @@
"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"
},
"sections": {
"addresses": "E-mail-címek"
}
},
"security": {
+3
View File
@@ -1830,6 +1830,9 @@
"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"
},
"sections": {
"addresses": "Indirizzi email"
}
},
"security": {
+3
View File
@@ -1830,6 +1830,9 @@
"title": "自分と共有",
"description": "管理できるグループアカウントと共有アカウントです。1つ選択して、フィルター、不在応答、カレンダー、連絡先を編集します。",
"shared_label": "共有アカウント"
},
"sections": {
"addresses": "メールアドレス"
}
},
"security": {
+3
View File
@@ -1830,6 +1830,9 @@
"title": "나와 공유됨",
"description": "관리할 수 있는 그룹 및 공유 계정입니다. 하나를 선택하여 필터, 부재 중 자동 응답, 캘린더, 연락처를 편집하세요.",
"shared_label": "공유 계정"
},
"sections": {
"addresses": "이메일 주소"
}
},
"security": {
+3
View File
@@ -1830,6 +1830,9 @@
"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"
},
"sections": {
"addresses": "E-pasta adreses"
}
},
"security": {
+3
View File
@@ -1833,6 +1833,9 @@
"title": "Надтай хуваалцсан",
"description": "Бүлэг болон хуваалцсан акаунтуудыг удирдах боломжтой. Шүүлтүүр, амралтын хариулагч, хуанли, харилцагчдыг засахын тулд нэгийг нь сонгоно уу.",
"shared_label": "Хуваалцсан бүртгэл"
},
"sections": {
"addresses": "Имэйл хаягууд"
}
},
"scoped": {
+3
View File
@@ -1833,6 +1833,9 @@
"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"
},
"sections": {
"addresses": "E-postadresser"
}
},
"scoped": {
+3
View File
@@ -1830,6 +1830,9 @@
"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"
},
"sections": {
"addresses": "E-mailadressen"
}
},
"security": {
+3
View File
@@ -1830,6 +1830,9 @@
"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"
},
"sections": {
"addresses": "Adresy e-mail"
}
},
"security": {
+3
View File
@@ -1830,6 +1830,9 @@
"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"
},
"sections": {
"addresses": "Endereços de e-mail"
}
},
"security": {
+3
View File
@@ -1833,6 +1833,9 @@
"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"
},
"sections": {
"addresses": "Adrese de e-mail"
}
},
"scoped": {
+3
View File
@@ -1830,6 +1830,9 @@
"title": "Доступные мне",
"description": "Групповые и общие учётные записи, которыми вы можете управлять. Выберите одну, чтобы изменить её фильтры, автоответчик, календари и контакты.",
"shared_label": "Общая учётная запись"
},
"sections": {
"addresses": "Адреса электронной почты"
}
},
"security": {
+3
View File
@@ -1833,6 +1833,9 @@
"title": "Zdieľané so mnou",
"description": "Skupinové a zdieľané účty, ktoré môžete spravovať.",
"shared_label": "Zdieľaný účet"
},
"sections": {
"addresses": "E-mailové adresy"
}
},
"scoped": {
+3
View File
@@ -1830,6 +1830,9 @@
"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"
},
"sections": {
"addresses": "E-posta adresleri"
}
},
"security": {
+3
View File
@@ -1830,6 +1830,9 @@
"title": "Доступні мені",
"description": "Групові та спільні облікові записи, якими ви можете керувати. Виберіть один, щоб змінити його фільтри, автовідповідач, календарі та контакти.",
"shared_label": "Спільний обліковий запис"
},
"sections": {
"addresses": "Адреси електронної пошти"
}
},
"security": {
+3
View File
@@ -1833,6 +1833,9 @@
"title": "與我共用",
"description": "您可以管理的群組與共用帳號。選取一個帳號,即可編輯其篩選規則、休假自動回覆、行事曆與聯絡人。",
"shared_label": "共用帳號"
},
"sections": {
"addresses": "電子郵件地址"
}
},
"scoped": {
+3
View File
@@ -1830,6 +1830,9 @@
"title": "与我共享",
"description": "您可以管理的群组和共享账户。选择一个以编辑其筛选器、休假自动回复、日历和联系人。",
"shared_label": "共享账户"
},
"sections": {
"addresses": "电子邮件地址"
}
},
"security": {