@djangocfg/layouts 2.1.590 → 2.1.592
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -12
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +37 -26
- package/src/layouts/PrivateLayout/README.md +20 -12
- package/src/layouts/PrivateLayout/components/PrivateContent.tsx +29 -42
- package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +23 -92
- package/src/layouts/PrivateLayout/components/PrivateSidebarAccount.tsx +32 -62
- package/src/layouts/PrivateLayout/components/SidebarBody.tsx +45 -0
- package/src/layouts/PrivateLayout/components/SidebarBrand.tsx +64 -152
- package/src/layouts/PrivateLayout/components/SidebarBrandSwitcher.tsx +2 -30
- package/src/layouts/PrivateLayout/components/SidebarNavGroup.tsx +55 -115
- package/src/layouts/PrivateLayout/components/SidebarNavItem.tsx +37 -100
- package/src/layouts/PrivateLayout/components/SidebarPeek.tsx +34 -0
- package/src/layouts/PrivateLayout/components/SidebarResizeHandle.tsx +76 -0
- package/src/layouts/PrivateLayout/components/SidebarSlots.tsx +14 -53
- package/src/layouts/PrivateLayout/components/index.ts +0 -4
- package/src/layouts/PrivateLayout/context.tsx +10 -115
- package/src/layouts/PrivateLayout/geometry.ts +17 -0
- package/src/layouts/PrivateLayout/hooks/index.ts +2 -1
- package/src/layouts/PrivateLayout/hooks/useLayoutVisual.ts +4 -4
- package/src/layouts/PrivateLayout/hooks/useSidebarKeyboard.ts +33 -93
- package/src/layouts/PrivateLayout/hooks/useSidebarPeek.tsx +151 -0
- package/src/layouts/PrivateLayout/hooks/useSidebarWidth.ts +83 -0
- package/src/layouts/PrivateLayout/types.ts +13 -20
- package/src/layouts/SettingsLayout/README.md +23 -10
- package/src/layouts/SettingsLayout/SettingsDialog.tsx +14 -8
- package/src/layouts/SettingsLayout/SettingsForm.tsx +13 -9
- package/src/layouts/SettingsLayout/components/ApiKeySection/ApiKeySection.tsx +9 -7
- package/src/layouts/SettingsLayout/components/ApiKeySection/context.tsx +6 -41
- package/src/layouts/SettingsLayout/components/AvatarSection.tsx +11 -3
- package/src/layouts/SettingsLayout/components/SettingsNav.tsx +5 -9
- package/src/layouts/SettingsLayout/components/SettingsNavItem.tsx +5 -7
- package/src/layouts/SettingsLayout/components/SettingsPanel.tsx +5 -10
- package/src/layouts/SettingsLayout/components/SettingsSearch.tsx +5 -3
- package/src/layouts/SettingsLayout/components/SettingsShell.tsx +14 -21
- package/src/layouts/SettingsLayout/components/TwoFactorSection/TwoFactorSection.tsx +52 -105
- package/src/layouts/SettingsLayout/components/index.ts +0 -1
- package/src/layouts/SettingsLayout/context/SettingsContext.tsx +5 -2
- package/src/layouts/SettingsLayout/i18n/index.ts +39 -0
- package/src/layouts/SettingsLayout/i18n/locales/ar.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/da.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/de.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/en.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/es.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/fr.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/it.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/ja.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/ko.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/nl.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/no.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/pl.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/ptBR.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/ru.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/sv.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/tr.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/zh.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/types.ts +65 -0
- package/src/layouts/SettingsLayout/index.ts +0 -1
- package/src/layouts/SettingsLayout/sections/AccountSection.tsx +9 -4
- package/src/layouts/SettingsLayout/sections/DeleteAccountRow.tsx +4 -1
- package/src/layouts/SettingsLayout/sections/GeneralSection.tsx +25 -0
- package/src/layouts/SettingsLayout/sections/PreferencesRows.tsx +12 -4
- package/src/layouts/SettingsLayout/sections/builtins.tsx +34 -29
- package/src/layouts/SettingsLayout/sections/index.ts +2 -2
- package/src/layouts/SettingsLayout/types.ts +12 -4
- package/src/layouts/types/layout.types.ts +5 -6
- package/src/styles/dashboard.css +10 -0
- package/src/layouts/PrivateLayout/density.ts +0 -48
- package/src/layouts/PrivateLayout/hooks/useHoverExpand.ts +0 -110
- package/src/layouts/PrivateLayout/hooks/useShellVisualState.ts +0 -130
- package/src/layouts/SettingsLayout/components/SettingsRailFooter.tsx +0 -74
- package/src/layouts/SettingsLayout/sections/SecuritySection.tsx +0 -15
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { Loader2,
|
|
3
|
+
import { Loader2, ShieldOff } from 'lucide-react';
|
|
4
4
|
import React, { useEffect, useState } from 'react';
|
|
5
5
|
|
|
6
6
|
import { useTwoFactorSetup, useTwoFactorStatus } from '@djangocfg/api/auth';
|
|
7
7
|
import {
|
|
8
8
|
Alert,
|
|
9
9
|
AlertDescription,
|
|
10
|
-
Badge,
|
|
11
10
|
Button,
|
|
12
11
|
OTPInput,
|
|
13
|
-
Preloader,
|
|
14
12
|
SettingRow,
|
|
15
13
|
SettingsBlock,
|
|
16
14
|
} from '@djangocfg/ui-core/components';
|
|
17
|
-
import { cn } from '@djangocfg/ui-core/lib';
|
|
18
15
|
|
|
16
|
+
import { fill, useSettingsCopy, type SettingsCopy } from '../../i18n';
|
|
19
17
|
import { TwoFactorSetup } from '../../../../snippets/TwoFactor';
|
|
20
18
|
|
|
21
19
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -24,24 +22,6 @@ import { TwoFactorSetup } from '../../../../snippets/TwoFactor';
|
|
|
24
22
|
|
|
25
23
|
type View = 'status' | 'setup';
|
|
26
24
|
|
|
27
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
-
// Status badge
|
|
29
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
30
|
-
|
|
31
|
-
function StatusBadge({ enabled }: { enabled: boolean }) {
|
|
32
|
-
return (
|
|
33
|
-
<Badge
|
|
34
|
-
variant={enabled ? 'default' : 'secondary'}
|
|
35
|
-
className={cn(
|
|
36
|
-
'text-xs font-medium',
|
|
37
|
-
enabled && 'bg-success-background text-success-foreground border-success-border',
|
|
38
|
-
)}
|
|
39
|
-
>
|
|
40
|
-
{enabled ? 'Enabled' : 'Disabled'}
|
|
41
|
-
</Badge>
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
25
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
46
26
|
// Disable confirmation — inline, not a dialog. Settings already lives in a
|
|
47
27
|
// dialog; stacking a second modal on top fights its focus trap and dims the
|
|
@@ -61,6 +41,7 @@ function DisableConfirm({
|
|
|
61
41
|
onCancel: () => void;
|
|
62
42
|
}) {
|
|
63
43
|
// Mounted fresh each time the confirmation opens — state starts clean.
|
|
44
|
+
const copy = useSettingsCopy().security;
|
|
64
45
|
const [code, setCode] = useState('');
|
|
65
46
|
|
|
66
47
|
return (
|
|
@@ -68,10 +49,9 @@ function DisableConfirm({
|
|
|
68
49
|
<div className="flex gap-2.5">
|
|
69
50
|
<ShieldOff className="mt-0.5 h-4 w-4 flex-shrink-0 text-destructive" />
|
|
70
51
|
<div className="space-y-1">
|
|
71
|
-
<p className="text-sm font-medium">
|
|
52
|
+
<p className="text-sm font-medium">{copy.disableTitle}</p>
|
|
72
53
|
<p className="text-[13px] leading-relaxed text-muted-foreground">
|
|
73
|
-
|
|
74
|
-
This will make your account less secure.
|
|
54
|
+
{copy.disableHint}
|
|
75
55
|
</p>
|
|
76
56
|
</div>
|
|
77
57
|
</div>
|
|
@@ -94,7 +74,7 @@ function DisableConfirm({
|
|
|
94
74
|
/>
|
|
95
75
|
<div className="flex gap-2">
|
|
96
76
|
<Button variant="outline" size="sm" onClick={onCancel} disabled={isLoading}>
|
|
97
|
-
|
|
77
|
+
{copy.cancel}
|
|
98
78
|
</Button>
|
|
99
79
|
<Button
|
|
100
80
|
variant="destructive"
|
|
@@ -103,9 +83,9 @@ function DisableConfirm({
|
|
|
103
83
|
disabled={isLoading || code.length !== 6}
|
|
104
84
|
>
|
|
105
85
|
{isLoading ? (
|
|
106
|
-
<><Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
86
|
+
<><Loader2 className="mr-2 h-4 w-4 animate-spin" />{copy.disabling}</>
|
|
107
87
|
) : (
|
|
108
|
-
|
|
88
|
+
copy.disable
|
|
109
89
|
)}
|
|
110
90
|
</Button>
|
|
111
91
|
</div>
|
|
@@ -118,9 +98,9 @@ function DisableConfirm({
|
|
|
118
98
|
// Helpers
|
|
119
99
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
120
100
|
|
|
121
|
-
function deviceDescription(createdAt: string, isPrimary: boolean): string {
|
|
122
|
-
const added =
|
|
123
|
-
return isPrimary ? `${added} ·
|
|
101
|
+
function deviceDescription(copy: SettingsCopy['security'], createdAt: string, isPrimary: boolean): string {
|
|
102
|
+
const added = fill(copy.deviceAdded, { date: new Date(createdAt).toLocaleDateString() });
|
|
103
|
+
return isPrimary ? `${added} · ${copy.devicePrimary}` : added;
|
|
124
104
|
}
|
|
125
105
|
|
|
126
106
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -128,6 +108,7 @@ function deviceDescription(createdAt: string, isPrimary: boolean): string {
|
|
|
128
108
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
129
109
|
|
|
130
110
|
export const TwoFactorSection: React.FC = () => {
|
|
111
|
+
const copy = useSettingsCopy().security;
|
|
131
112
|
const [view, setView] = useState<View>('status');
|
|
132
113
|
const [showDisable, setShowDisable] = useState(false);
|
|
133
114
|
|
|
@@ -170,13 +151,13 @@ export const TwoFactorSection: React.FC = () => {
|
|
|
170
151
|
// visible (Preloader-based) loading state.
|
|
171
152
|
if (view === 'setup') {
|
|
172
153
|
return (
|
|
173
|
-
<SettingsBlock title=
|
|
154
|
+
<SettingsBlock title={copy.setupTitle}>
|
|
174
155
|
<div className="flex items-center justify-between pb-2">
|
|
175
156
|
<p className="text-[13px] leading-relaxed text-muted-foreground">
|
|
176
|
-
|
|
157
|
+
{copy.setupHint}
|
|
177
158
|
</p>
|
|
178
159
|
<Button variant="ghost" size="sm" onClick={() => setView('status')}>
|
|
179
|
-
|
|
160
|
+
{copy.cancel}
|
|
180
161
|
</Button>
|
|
181
162
|
</div>
|
|
182
163
|
<div className="flex justify-center py-2">
|
|
@@ -189,22 +170,15 @@ export const TwoFactorSection: React.FC = () => {
|
|
|
189
170
|
);
|
|
190
171
|
}
|
|
191
172
|
|
|
192
|
-
// ── Loading skeleton ────────────────────────────────────────────────────────
|
|
193
|
-
if (isLoading && has2FAEnabled === null) {
|
|
194
|
-
return <Preloader variant="inline" className="py-10" />;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
173
|
// ── Status view ─────────────────────────────────────────────────────────────
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
</span>
|
|
207
|
-
);
|
|
174
|
+
// One compact block: a single status row (state + action), the inline confirm
|
|
175
|
+
// when turning off, then the connected devices as plain rows.
|
|
176
|
+
const unknown = has2FAEnabled === null;
|
|
177
|
+
const statusDescription = unknown
|
|
178
|
+
? isLoading ? copy.checking : copy.unavailable
|
|
179
|
+
: has2FAEnabled
|
|
180
|
+
? copy.on
|
|
181
|
+
: copy.off;
|
|
208
182
|
|
|
209
183
|
const statusAction = has2FAEnabled ? (
|
|
210
184
|
<Button
|
|
@@ -212,70 +186,43 @@ export const TwoFactorSection: React.FC = () => {
|
|
|
212
186
|
size="sm"
|
|
213
187
|
onClick={() => { clearError(); setShowDisable(true); }}
|
|
214
188
|
disabled={isLoading || showDisable}
|
|
215
|
-
className="text-destructive hover:text-destructive hover:bg-destructive/10 border-destructive/30"
|
|
216
189
|
>
|
|
217
|
-
|
|
190
|
+
{copy.turnOff}
|
|
218
191
|
</Button>
|
|
219
192
|
) : (
|
|
220
|
-
<Button size="sm" onClick={handleEnableClick} disabled={isLoading}>
|
|
221
|
-
|
|
193
|
+
<Button size="sm" onClick={handleEnableClick} disabled={isLoading || unknown}>
|
|
194
|
+
{copy.turnOn}
|
|
222
195
|
</Button>
|
|
223
196
|
);
|
|
224
197
|
|
|
225
198
|
return (
|
|
226
|
-
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
{
|
|
236
|
-
{
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
onCancel={handleDisableCancel}
|
|
242
|
-
/>
|
|
243
|
-
)}
|
|
244
|
-
|
|
245
|
-
{/* Fetch error */}
|
|
246
|
-
{error && !showDisable && (
|
|
247
|
-
<div className="py-3">
|
|
248
|
-
<Alert variant="destructive">
|
|
249
|
-
<AlertDescription>{error}</AlertDescription>
|
|
250
|
-
</Alert>
|
|
251
|
-
</div>
|
|
252
|
-
)}
|
|
199
|
+
<SettingsBlock title={copy.title}>
|
|
200
|
+
<SettingRow
|
|
201
|
+
label={copy.twoFactor}
|
|
202
|
+
description={statusDescription}
|
|
203
|
+
action={statusAction}
|
|
204
|
+
/>
|
|
205
|
+
|
|
206
|
+
{showDisable && (
|
|
207
|
+
<DisableConfirm
|
|
208
|
+
isLoading={isLoading}
|
|
209
|
+
error={error}
|
|
210
|
+
onConfirm={handleDisableConfirm}
|
|
211
|
+
onCancel={handleDisableCancel}
|
|
212
|
+
/>
|
|
213
|
+
)}
|
|
253
214
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
<Shield className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
|
258
|
-
<span>
|
|
259
|
-
Two-factor authentication adds a second verification step when signing in,
|
|
260
|
-
protecting your account even if your password is compromised.
|
|
261
|
-
</span>
|
|
262
|
-
</p>
|
|
263
|
-
)}
|
|
264
|
-
</SettingsBlock>
|
|
215
|
+
{error && !showDisable && (
|
|
216
|
+
<p className="py-2 text-[13px] text-destructive">{error}</p>
|
|
217
|
+
)}
|
|
265
218
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
/>
|
|
275
|
-
))}
|
|
276
|
-
</SettingsBlock>
|
|
277
|
-
)}
|
|
278
|
-
</div>
|
|
279
|
-
</>
|
|
219
|
+
{has2FAEnabled && devices.map((device) => (
|
|
220
|
+
<SettingRow
|
|
221
|
+
key={device.id}
|
|
222
|
+
label={device.name}
|
|
223
|
+
description={deviceDescription(copy, device.createdAt, device.isPrimary)}
|
|
224
|
+
/>
|
|
225
|
+
))}
|
|
226
|
+
</SettingsBlock>
|
|
280
227
|
);
|
|
281
228
|
};
|
|
@@ -40,6 +40,9 @@ interface SettingsContextValue {
|
|
|
40
40
|
searchable: boolean;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/** `searchable: 'auto'` shows the search box only when the rail is long enough to need it. */
|
|
44
|
+
export const SEARCH_MIN_SECTIONS = 10;
|
|
45
|
+
|
|
43
46
|
const SettingsContext = createContext<SettingsContextValue | null>(null);
|
|
44
47
|
|
|
45
48
|
export const useSettingsContext = (): SettingsContextValue => {
|
|
@@ -54,7 +57,7 @@ export interface SettingsProviderProps {
|
|
|
54
57
|
/** Fully-merged sections (built-ins + app), pre-concatenated by the caller. */
|
|
55
58
|
sections: SettingsSection[];
|
|
56
59
|
groups?: SettingsGroup[];
|
|
57
|
-
searchable: boolean;
|
|
60
|
+
searchable: boolean | 'auto';
|
|
58
61
|
/** Controlled active id (dialog mode passes the store value). */
|
|
59
62
|
activeId: string | null;
|
|
60
63
|
/** Called when active section changes (dialog mode writes the store). */
|
|
@@ -113,7 +116,7 @@ export const SettingsProvider: React.FC<SettingsProviderProps> = ({
|
|
|
113
116
|
setActive,
|
|
114
117
|
query,
|
|
115
118
|
setQuery,
|
|
116
|
-
searchable,
|
|
119
|
+
searchable: searchable === 'auto' ? visible.length >= SEARCH_MIN_SECTIONS : searchable,
|
|
117
120
|
}),
|
|
118
121
|
[title, resolvedGroups, visible, active, resolvedActiveId, setActive, query, searchable],
|
|
119
122
|
);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createCopy, type Locale } from '@djangocfg/i18n/copy';
|
|
4
|
+
|
|
5
|
+
import { ar } from './locales/ar';
|
|
6
|
+
import { da } from './locales/da';
|
|
7
|
+
import { de } from './locales/de';
|
|
8
|
+
import { en } from './locales/en';
|
|
9
|
+
import { es } from './locales/es';
|
|
10
|
+
import { fr } from './locales/fr';
|
|
11
|
+
import { it } from './locales/it';
|
|
12
|
+
import { ja } from './locales/ja';
|
|
13
|
+
import { ko } from './locales/ko';
|
|
14
|
+
import { nl } from './locales/nl';
|
|
15
|
+
import { no } from './locales/no';
|
|
16
|
+
import { pl } from './locales/pl';
|
|
17
|
+
import { ptBR } from './locales/ptBR';
|
|
18
|
+
import { ru } from './locales/ru';
|
|
19
|
+
import { sv } from './locales/sv';
|
|
20
|
+
import { tr } from './locales/tr';
|
|
21
|
+
import { zh } from './locales/zh';
|
|
22
|
+
|
|
23
|
+
import type { SettingsCopy } from './types';
|
|
24
|
+
|
|
25
|
+
export type { SettingsCopy } from './types';
|
|
26
|
+
export { fill } from '@djangocfg/i18n/copy';
|
|
27
|
+
|
|
28
|
+
/** Every bundled dictionary, typed complete over the shared `Locale` union. */
|
|
29
|
+
export const SETTINGS_DICTIONARIES: Readonly<Record<Locale, SettingsCopy>> = {
|
|
30
|
+
en, ru, ko, ja, de, fr, zh, it, es, nl, ar, tr, pl, sv, no, da,
|
|
31
|
+
'pt-BR': ptBR,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** Settings copy for the active language: the `@djangocfg/i18n` provider, else English. */
|
|
35
|
+
const kit = createCopy(SETTINGS_DICTIONARIES);
|
|
36
|
+
export const useSettingsCopy = kit.useCopy;
|
|
37
|
+
|
|
38
|
+
/** The bundled locale `useSettingsCopy` resolved to, for date formatting. */
|
|
39
|
+
export const useSettingsLocale = kit.useLocale;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { SettingsCopy } from '../types';
|
|
2
|
+
|
|
3
|
+
export const ar: SettingsCopy = {
|
|
4
|
+
nav: {
|
|
5
|
+
title: "الإعدادات",
|
|
6
|
+
platform: "المنصة",
|
|
7
|
+
account: "الحساب",
|
|
8
|
+
preferences: "التفضيلات",
|
|
9
|
+
apiKeys: "مفاتيح API",
|
|
10
|
+
apiKeysDescription: "أنشئ المفاتيح وأدرها للوصول البرمجي.",
|
|
11
|
+
},
|
|
12
|
+
prefs: {
|
|
13
|
+
general: "عام",
|
|
14
|
+
other: "أخرى",
|
|
15
|
+
language: "اللغة",
|
|
16
|
+
timeZone: "المنطقة الزمنية",
|
|
17
|
+
theme: "المظهر",
|
|
18
|
+
},
|
|
19
|
+
account: {
|
|
20
|
+
dangerZone: "منطقة الخطر",
|
|
21
|
+
deleteAccountDescription: "احذف حسابك وجميع البيانات المرتبطة به نهائيًا.",
|
|
22
|
+
changeAvatar: "تغيير الصورة الرمزية",
|
|
23
|
+
},
|
|
24
|
+
search: {
|
|
25
|
+
clear: "مسح البحث",
|
|
26
|
+
},
|
|
27
|
+
security: {
|
|
28
|
+
title: "الأمان",
|
|
29
|
+
twoFactor: "المصادقة الثنائية",
|
|
30
|
+
on: "مفعّلة",
|
|
31
|
+
off: "متوقفة · تضيف خطوة ثانية عند تسجيل الدخول",
|
|
32
|
+
checking: "جارٍ التحقق…",
|
|
33
|
+
unavailable: "غير متاحة",
|
|
34
|
+
turnOn: "تفعيل",
|
|
35
|
+
turnOff: "إيقاف",
|
|
36
|
+
setupTitle: "إعداد المصادقة الثنائية",
|
|
37
|
+
setupHint: "امسح رمز QR بتطبيق المصادقة (Google Authenticator أو Authy وغيرهما)",
|
|
38
|
+
cancel: "إلغاء",
|
|
39
|
+
deviceAdded: "أُضيف في {{date}}",
|
|
40
|
+
devicePrimary: "أساسي",
|
|
41
|
+
disableTitle: "إيقاف المصادقة الثنائية؟",
|
|
42
|
+
disableHint: "أدخل الرمز المكوّن من 6 أرقام من تطبيق المصادقة للتأكيد. سيجعل ذلك حسابك أقل أمانًا.",
|
|
43
|
+
disabling: "جارٍ الإيقاف…",
|
|
44
|
+
disable: "إيقاف المصادقة الثنائية",
|
|
45
|
+
},
|
|
46
|
+
apiKey: {
|
|
47
|
+
title: "مفتاح API",
|
|
48
|
+
description: "مفتاح API الشخصي للوصول البرمجي",
|
|
49
|
+
created: "تاريخ الإنشاء",
|
|
50
|
+
reissued: "آخر إعادة إصدار",
|
|
51
|
+
neverReissued: "لم يُعاد إصداره قط",
|
|
52
|
+
regenerate: "إعادة إنشاء مفتاح API",
|
|
53
|
+
confirmRegenerate: "انقر للتأكيد",
|
|
54
|
+
regenerating: "جارٍ إعادة الإنشاء…",
|
|
55
|
+
regenerated: "تمت إعادة إنشاء مفتاح API",
|
|
56
|
+
failedToRegenerate: "تعذّرت إعادة إنشاء مفتاح API",
|
|
57
|
+
done: "تم",
|
|
58
|
+
test: "اختبار مفتاح API",
|
|
59
|
+
testing: "جارٍ الاختبار…",
|
|
60
|
+
testSuccess: "مفتاح API صالح",
|
|
61
|
+
testFailed: "فشل اختبار مفتاح API",
|
|
62
|
+
keyRow: "مفتاح API",
|
|
63
|
+
active: "نشط",
|
|
64
|
+
freshKey: "هذا هو مفتاح API الجديد، انسخه الآن. سيُخفى عند مغادرة هذه الصفحة.",
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { SettingsCopy } from '../types';
|
|
2
|
+
|
|
3
|
+
export const da: SettingsCopy = {
|
|
4
|
+
nav: {
|
|
5
|
+
title: "Indstillinger",
|
|
6
|
+
platform: "Platform",
|
|
7
|
+
account: "Konto",
|
|
8
|
+
preferences: "Indstillinger",
|
|
9
|
+
apiKeys: "API-nøgler",
|
|
10
|
+
apiKeysDescription: "Opret og administrer nøgler til programmatisk adgang.",
|
|
11
|
+
},
|
|
12
|
+
prefs: {
|
|
13
|
+
general: "Generelt",
|
|
14
|
+
other: "Andet",
|
|
15
|
+
language: "Sprog",
|
|
16
|
+
timeZone: "Tidszone",
|
|
17
|
+
theme: "Tema",
|
|
18
|
+
},
|
|
19
|
+
account: {
|
|
20
|
+
dangerZone: "Farezone",
|
|
21
|
+
deleteAccountDescription: "Slet din konto og alle tilknyttede data permanent.",
|
|
22
|
+
changeAvatar: "Skift avatar",
|
|
23
|
+
},
|
|
24
|
+
search: {
|
|
25
|
+
clear: "Ryd søgning",
|
|
26
|
+
},
|
|
27
|
+
security: {
|
|
28
|
+
title: "Sikkerhed",
|
|
29
|
+
twoFactor: "Tofaktorgodkendelse",
|
|
30
|
+
on: "Til",
|
|
31
|
+
off: "Fra · tilføjer et ekstra trin ved login",
|
|
32
|
+
checking: "Kontrollerer…",
|
|
33
|
+
unavailable: "Ikke tilgængelig",
|
|
34
|
+
turnOn: "Slå til",
|
|
35
|
+
turnOff: "Slå fra",
|
|
36
|
+
setupTitle: "Opsæt tofaktorgodkendelse",
|
|
37
|
+
setupHint: "Scan QR-koden med din godkendelsesapp (Google Authenticator, Authy osv.)",
|
|
38
|
+
cancel: "Annuller",
|
|
39
|
+
deviceAdded: "Tilføjet {{date}}",
|
|
40
|
+
devicePrimary: "Primær",
|
|
41
|
+
disableTitle: "Slå tofaktorgodkendelse fra?",
|
|
42
|
+
disableHint: "Indtast den 6-cifrede kode fra din godkendelsesapp for at bekræfte. Din konto bliver mindre sikker.",
|
|
43
|
+
disabling: "Slår fra…",
|
|
44
|
+
disable: "Slå 2FA fra",
|
|
45
|
+
},
|
|
46
|
+
apiKey: {
|
|
47
|
+
title: "API-nøgle",
|
|
48
|
+
description: "Din personlige API-nøgle til programmatisk adgang",
|
|
49
|
+
created: "Oprettet",
|
|
50
|
+
reissued: "Sidst udstedt igen",
|
|
51
|
+
neverReissued: "Aldrig udstedt igen",
|
|
52
|
+
regenerate: "Generer API-nøgle igen",
|
|
53
|
+
confirmRegenerate: "Klik for at bekræfte",
|
|
54
|
+
regenerating: "Genererer igen…",
|
|
55
|
+
regenerated: "API-nøglen blev genereret igen",
|
|
56
|
+
failedToRegenerate: "API-nøglen kunne ikke genereres igen",
|
|
57
|
+
done: "Færdig",
|
|
58
|
+
test: "Test API-nøgle",
|
|
59
|
+
testing: "Tester…",
|
|
60
|
+
testSuccess: "API-nøglen er gyldig",
|
|
61
|
+
testFailed: "API-nøgletesten mislykkedes",
|
|
62
|
+
keyRow: "API-nøgle",
|
|
63
|
+
active: "Aktiv",
|
|
64
|
+
freshKey: "Det er din nye API-nøgle – kopier den nu. Den skjules, når du forlader siden.",
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { SettingsCopy } from '../types';
|
|
2
|
+
|
|
3
|
+
export const de: SettingsCopy = {
|
|
4
|
+
nav: {
|
|
5
|
+
title: "Einstellungen",
|
|
6
|
+
platform: "Plattform",
|
|
7
|
+
account: "Konto",
|
|
8
|
+
preferences: "Einstellungen",
|
|
9
|
+
apiKeys: "API-Schlüssel",
|
|
10
|
+
apiKeysDescription: "Schlüssel für den programmatischen Zugriff erstellen und verwalten.",
|
|
11
|
+
},
|
|
12
|
+
prefs: {
|
|
13
|
+
general: "Allgemein",
|
|
14
|
+
other: "Sonstiges",
|
|
15
|
+
language: "Sprache",
|
|
16
|
+
timeZone: "Zeitzone",
|
|
17
|
+
theme: "Design",
|
|
18
|
+
},
|
|
19
|
+
account: {
|
|
20
|
+
dangerZone: "Gefahrenbereich",
|
|
21
|
+
deleteAccountDescription: "Konto und alle zugehörigen Daten dauerhaft löschen.",
|
|
22
|
+
changeAvatar: "Avatar ändern",
|
|
23
|
+
},
|
|
24
|
+
search: {
|
|
25
|
+
clear: "Suche löschen",
|
|
26
|
+
},
|
|
27
|
+
security: {
|
|
28
|
+
title: "Sicherheit",
|
|
29
|
+
twoFactor: "Zwei-Faktor-Authentifizierung",
|
|
30
|
+
on: "An",
|
|
31
|
+
off: "Aus · fügt beim Anmelden einen zweiten Schritt hinzu",
|
|
32
|
+
checking: "Wird geprüft…",
|
|
33
|
+
unavailable: "Nicht verfügbar",
|
|
34
|
+
turnOn: "Einschalten",
|
|
35
|
+
turnOff: "Ausschalten",
|
|
36
|
+
setupTitle: "Zwei-Faktor-Authentifizierung einrichten",
|
|
37
|
+
setupHint: "Scannen Sie den QR-Code mit Ihrer Authenticator-App (Google Authenticator, Authy usw.)",
|
|
38
|
+
cancel: "Abbrechen",
|
|
39
|
+
deviceAdded: "Hinzugefügt am {{date}}",
|
|
40
|
+
devicePrimary: "Primär",
|
|
41
|
+
disableTitle: "Zwei-Faktor-Authentifizierung deaktivieren?",
|
|
42
|
+
disableHint: "Geben Sie zur Bestätigung den 6-stelligen Code Ihrer Authenticator-App ein. Ihr Konto ist dann weniger sicher.",
|
|
43
|
+
disabling: "Wird deaktiviert…",
|
|
44
|
+
disable: "2FA deaktivieren",
|
|
45
|
+
},
|
|
46
|
+
apiKey: {
|
|
47
|
+
title: "API-Schlüssel",
|
|
48
|
+
description: "Ihr persönlicher API-Schlüssel für den programmatischen Zugriff",
|
|
49
|
+
created: "Erstellt",
|
|
50
|
+
reissued: "Zuletzt neu ausgestellt",
|
|
51
|
+
neverReissued: "Nie neu ausgestellt",
|
|
52
|
+
regenerate: "API-Schlüssel neu erzeugen",
|
|
53
|
+
confirmRegenerate: "Zum Bestätigen klicken",
|
|
54
|
+
regenerating: "Wird neu erzeugt…",
|
|
55
|
+
regenerated: "API-Schlüssel neu erzeugt",
|
|
56
|
+
failedToRegenerate: "API-Schlüssel konnte nicht neu erzeugt werden",
|
|
57
|
+
done: "Fertig",
|
|
58
|
+
test: "API-Schlüssel testen",
|
|
59
|
+
testing: "Wird getestet…",
|
|
60
|
+
testSuccess: "API-Schlüssel ist gültig",
|
|
61
|
+
testFailed: "API-Schlüssel-Test fehlgeschlagen",
|
|
62
|
+
keyRow: "API-Schlüssel",
|
|
63
|
+
active: "Aktiv",
|
|
64
|
+
freshKey: "Das ist Ihr neuer API-Schlüssel – kopieren Sie ihn jetzt. Beim Verlassen der Seite wird er ausgeblendet.",
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { SettingsCopy } from '../types';
|
|
2
|
+
|
|
3
|
+
export const en: SettingsCopy = {
|
|
4
|
+
nav: {
|
|
5
|
+
title: "Settings",
|
|
6
|
+
platform: "Platform",
|
|
7
|
+
account: "Account",
|
|
8
|
+
preferences: "Preferences",
|
|
9
|
+
apiKeys: "API keys",
|
|
10
|
+
apiKeysDescription: "Create and manage keys for programmatic access.",
|
|
11
|
+
},
|
|
12
|
+
prefs: {
|
|
13
|
+
general: "General",
|
|
14
|
+
other: "Other",
|
|
15
|
+
language: "Language",
|
|
16
|
+
timeZone: "Time zone",
|
|
17
|
+
theme: "Theme",
|
|
18
|
+
},
|
|
19
|
+
account: {
|
|
20
|
+
dangerZone: "Danger zone",
|
|
21
|
+
deleteAccountDescription: "Permanently remove your account and all associated data.",
|
|
22
|
+
changeAvatar: "Change avatar",
|
|
23
|
+
},
|
|
24
|
+
search: {
|
|
25
|
+
clear: "Clear search",
|
|
26
|
+
},
|
|
27
|
+
security: {
|
|
28
|
+
title: "Security",
|
|
29
|
+
twoFactor: "Two-factor authentication",
|
|
30
|
+
on: "On",
|
|
31
|
+
off: "Off · adds a second step when you sign in",
|
|
32
|
+
checking: "Checking…",
|
|
33
|
+
unavailable: "Unavailable",
|
|
34
|
+
turnOn: "Turn on",
|
|
35
|
+
turnOff: "Turn off",
|
|
36
|
+
setupTitle: "Set up two-factor authentication",
|
|
37
|
+
setupHint: "Scan the QR code with your authenticator app (Google Authenticator, Authy, etc.)",
|
|
38
|
+
cancel: "Cancel",
|
|
39
|
+
deviceAdded: "Added {{date}}",
|
|
40
|
+
devicePrimary: "Primary",
|
|
41
|
+
disableTitle: "Disable two-factor authentication?",
|
|
42
|
+
disableHint: "Enter the 6-digit code from your authenticator app to confirm. This will make your account less secure.",
|
|
43
|
+
disabling: "Disabling…",
|
|
44
|
+
disable: "Disable 2FA",
|
|
45
|
+
},
|
|
46
|
+
apiKey: {
|
|
47
|
+
title: "API key",
|
|
48
|
+
description: "Your personal API key for programmatic access",
|
|
49
|
+
created: "Created",
|
|
50
|
+
reissued: "Last reissued",
|
|
51
|
+
neverReissued: "Never reissued",
|
|
52
|
+
regenerate: "Regenerate API key",
|
|
53
|
+
confirmRegenerate: "Click to confirm",
|
|
54
|
+
regenerating: "Regenerating…",
|
|
55
|
+
regenerated: "API key regenerated",
|
|
56
|
+
failedToRegenerate: "Failed to regenerate API key",
|
|
57
|
+
done: "Done",
|
|
58
|
+
test: "Test API key",
|
|
59
|
+
testing: "Testing…",
|
|
60
|
+
testSuccess: "API key is valid",
|
|
61
|
+
testFailed: "API key test failed",
|
|
62
|
+
keyRow: "API key",
|
|
63
|
+
active: "Active",
|
|
64
|
+
freshKey: "This is your new API key — copy it now. It will be masked when you leave this page.",
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { SettingsCopy } from '../types';
|
|
2
|
+
|
|
3
|
+
export const es: SettingsCopy = {
|
|
4
|
+
nav: {
|
|
5
|
+
title: "Ajustes",
|
|
6
|
+
platform: "Plataforma",
|
|
7
|
+
account: "Cuenta",
|
|
8
|
+
preferences: "Preferencias",
|
|
9
|
+
apiKeys: "Claves API",
|
|
10
|
+
apiKeysDescription: "Crea y gestiona claves para el acceso programático.",
|
|
11
|
+
},
|
|
12
|
+
prefs: {
|
|
13
|
+
general: "General",
|
|
14
|
+
other: "Otros",
|
|
15
|
+
language: "Idioma",
|
|
16
|
+
timeZone: "Zona horaria",
|
|
17
|
+
theme: "Tema",
|
|
18
|
+
},
|
|
19
|
+
account: {
|
|
20
|
+
dangerZone: "Zona de peligro",
|
|
21
|
+
deleteAccountDescription: "Elimina de forma permanente tu cuenta y todos los datos asociados.",
|
|
22
|
+
changeAvatar: "Cambiar avatar",
|
|
23
|
+
},
|
|
24
|
+
search: {
|
|
25
|
+
clear: "Borrar búsqueda",
|
|
26
|
+
},
|
|
27
|
+
security: {
|
|
28
|
+
title: "Seguridad",
|
|
29
|
+
twoFactor: "Autenticación en dos pasos",
|
|
30
|
+
on: "Activada",
|
|
31
|
+
off: "Desactivada · añade un segundo paso al iniciar sesión",
|
|
32
|
+
checking: "Comprobando…",
|
|
33
|
+
unavailable: "No disponible",
|
|
34
|
+
turnOn: "Activar",
|
|
35
|
+
turnOff: "Desactivar",
|
|
36
|
+
setupTitle: "Configurar la autenticación en dos pasos",
|
|
37
|
+
setupHint: "Escanea el código QR con tu aplicación de autenticación (Google Authenticator, Authy, etc.)",
|
|
38
|
+
cancel: "Cancelar",
|
|
39
|
+
deviceAdded: "Añadido el {{date}}",
|
|
40
|
+
devicePrimary: "Principal",
|
|
41
|
+
disableTitle: "¿Desactivar la autenticación en dos pasos?",
|
|
42
|
+
disableHint: "Introduce el código de 6 dígitos de tu aplicación de autenticación para confirmar. Tu cuenta será menos segura.",
|
|
43
|
+
disabling: "Desactivando…",
|
|
44
|
+
disable: "Desactivar 2FA",
|
|
45
|
+
},
|
|
46
|
+
apiKey: {
|
|
47
|
+
title: "Clave API",
|
|
48
|
+
description: "Tu clave API personal para el acceso programático",
|
|
49
|
+
created: "Creada",
|
|
50
|
+
reissued: "Última regeneración",
|
|
51
|
+
neverReissued: "Nunca regenerada",
|
|
52
|
+
regenerate: "Regenerar clave API",
|
|
53
|
+
confirmRegenerate: "Haz clic para confirmar",
|
|
54
|
+
regenerating: "Regenerando…",
|
|
55
|
+
regenerated: "Clave API regenerada",
|
|
56
|
+
failedToRegenerate: "No se pudo regenerar la clave API",
|
|
57
|
+
done: "Hecho",
|
|
58
|
+
test: "Probar clave API",
|
|
59
|
+
testing: "Probando…",
|
|
60
|
+
testSuccess: "La clave API es válida",
|
|
61
|
+
testFailed: "Falló la prueba de la clave API",
|
|
62
|
+
keyRow: "Clave API",
|
|
63
|
+
active: "Activa",
|
|
64
|
+
freshKey: "Esta es tu nueva clave API: cópiala ahora. Se ocultará al salir de esta página.",
|
|
65
|
+
},
|
|
66
|
+
};
|